import csv
import math
import matplotlib.pyplot as plt
import visdom
import numpy as np

DEMAND_CSV = "./data/demand.csv"
SITE_BANDWIDTH_CSV = "./data/site_bandwidth.csv"
QOS_CSV = "./data/qos.csv"
CONFIG_INI = "./data/config.ini"
# DEMAND_CSV = "./pressure_data/demand.csv"
# SITE_BANDWIDTH_CSV = "./pressure_data/site_bandwidth.csv"
# QOS_CSV = "./pressure_data/qos.csv"
# CONFIG_INI = "./pressure_data/config.ini"
OUTPUT_FILE = "C:\\Users\\DaChong\\Desktop\\solution.txt"


def getClientNodeAndTimeSeqs():
    with open(DEMAND_CSV) as f:
        demand_csv = csv.reader(f)
        clientNames = next(demand_csv)[1:]  # 客户节点名
        timeSeqs = [row[0] for row in demand_csv]  # 时间序列
    clientNodes = [{'name': name} for name in clientNames]
    return clientNodes, timeSeqs

def getEdgeNodes():
    with open(QOS_CSV) as f:
        qos_csv = csv.reader(f)
        next(qos_csv)
        edgeNames = [row[0] for row in qos_csv]  # 边缘节点名
    with open(SITE_BANDWIDTH_CSV) as f:
        bandwidth_csv = csv.reader(f)
        next(bandwidth_csv)
        bandwidths = {row[0]: int(row[1]) for row in bandwidth_csv}  # 边缘节点的带宽

    edgeNodes = [{'name': name} for name in edgeNames]
    for node in edgeNodes:
        node['bandwidth'] = bandwidths[node['name']]
    return edgeNodes

def decodeString(s: str):  # 解析<A,123>,<B,1234>,<Dm,12>
    begin, end = 0, 0
    result = {}
    if s=='\n' or s=='\r\n': return result
    while (begin > -1 and begin < len(s)):
        end = s.find('>', begin + 1)
        context = s[begin + 1:end].split(',')
        eName, size = context[0], context[1]
        result[eName] = int(size)
        begin = end + 2
    return result

def readSolutionTxtFile(T: int, M: int):
    plans = []
    with open(OUTPUT_FILE) as f:
        for t in range(0, T):
            client_plan = {}
            for m in range(0, M):
                line = f.readline().split(':')
                cName, line = line[0], line[1]
                plan = decodeString(line)
                client_plan[cName] = plan
            plans.append(client_plan)
    return plans

# 计算每个边缘节点，每个客户的分配序列
def getEdgeNodeSeqs(plans: list, edgeNodes: list, clientNodes: list, T: int):
    for edgeNode in edgeNodes:
        eName = edgeNode['name']
        distributeSeqs_clients = {clientNode['name']: [] for clientNode in clientNodes}
        distributeSeqs_total = []
        for cName, seq in distributeSeqs_clients.items():
            for t in range(0, T):
                plan = plans[t][cName]
                if eName in plan.keys():
                    seq.append(plan[eName])
                else:
                    seq.append(0)
        for t in range(0, T):
            total = 0
            for cName, seq in distributeSeqs_clients.items():
                total += seq[t]
            distributeSeqs_total.append(total)
        edgeNode['distributeSeqs_total'] = distributeSeqs_total
        edgeNode['distributeSeqs_clients'] = distributeSeqs_clients

def analysis(edgeNodes: list, T: int):
    times = [i + 1 for i in range(0, T)]
    for idx, edgeNode in enumerate(edgeNodes):
        eName = edgeNode['name']
        bandwidth = edgeNode['bandwidth']
        seqs_total = edgeNode['distributeSeqs_total']
        seqs_clients = edgeNode['distributeSeqs_clients']
        plt.title('Edge Node Name: {}'.format(eName))
        plt.gcf().subplots_adjust(bottom=0.16)
        plt.gcf().subplots_adjust(left=0.18)
        plt.xticks(fontproperties='Times New Roman', size=18)
        plt.yticks(fontproperties='Times New Roman', size=18)
        plt.xlabel("Time Sequence", fontdict={'family': 'Times New Roman', 'size': 20})
        plt.ylabel("Bandwidth", fontdict={'family': 'Times New Roman', 'size': 20})
        plt.grid(True, linestyle='-.')
        history_seqs = np.zeros(T).tolist()
        for cName, seqs in seqs_clients.items():
            plt.bar(times, seqs, bottom=history_seqs, label=cName)
            history_seqs = np.sum([history_seqs, seqs], axis=0).tolist()

        # _, axs = plt.subplots()
        # plt.bar(times, seqs_total)
        # plt.title('Edge Node Name: {}'.format(eName))
        # plt.gcf().subplots_adjust(bottom=0.16)
        # plt.gcf().subplots_adjust(left=0.18)
        # plt.xticks(fontproperties='Times New Roman', size=18)
        # plt.yticks(fontproperties='Times New Roman', size=18)
        # plt.xlabel("Time Sequence", fontdict={'family': 'Times New Roman', 'size': 20})
        # plt.ylabel("Bandwidth", fontdict={'family': 'Times New Roman', 'size': 20})
        # plt.grid(True, linestyle='-.')
        plt.legend()
        plt.show()

def analysis2(edgeNodes: list, T: int, sort=False):
    times = [i + 1 for i in range(0, T)]
    # 绘制每个边缘节点的带宽序列
    for idx, edgeNode in enumerate(edgeNodes):
        eName = edgeNode['name']
        bandwidth = edgeNode['bandwidth']
        seqs_clients = edgeNode['distributeSeqs_clients']
        cNames, results = [], []
        for cName,seqs in seqs_clients.items():
            cNames.append(cName)
            results.append(seqs)
        results = np.array(results).T # T*M
        if sort:
            seqs_total = edgeNode['distributeSeqs_total']
            seqs_sort_idx = [e[0] for e in sorted(enumerate(seqs_total), key=lambda x:x[1])]#排序
            results = results[seqs_sort_idx]

        title = '边缘节点: {}（带宽上限 {}）'.format(eName, bandwidth)
        win = "Node: {}".format(eName)
        viz.bar(
            X=results,
            opts=dict(
                stacked=True, width=620, title=title,
                xlabel='时间序列（Time Sequence）',
                ylabel='带宽（Band Width）',
                legend=cNames,
                xtickstep=10,
                rownames=times,
            ),
            win=win
        )

def analysis3(edgeNodes: list, T: int):
    p95 = math.ceil(T * 0.95) - 1                          # 时间序列中95%的位置，从0开始
    # 绘制每个边缘节点的占用情况
    for idx, edgeNode in enumerate(edgeNodes):
        times = range(0, T)
        eName = edgeNode['name']                          # 该边缘节点名称
        bandwidth = edgeNode['bandwidth']                 # 该边缘节点带宽上限
        seqs_total = edgeNode['distributeSeqs_total']     # 该边缘节点带宽序列，长度为 T
        seqs_clients = edgeNode['distributeSeqs_clients'] # 该边缘节点上每个客户的流量分配序列
        seqs_total = sorted(enumerate(seqs_total), key=lambda x: x[1])# 边缘节点带宽序列排序
        seqs_sort_idx = [e[0] for e in seqs_total]
        seqs_total = [e[1] for e in seqs_total]
        cNames, results = [], []                          # cNames表示客户节点名，results表示客户节点的流量分配序列
        for cName,seqs in seqs_clients.items():
            cNames.append(cName)
            results.append(seqs)
        results = np.array(results).T          # 形状为 T*M
        # 排序后的结果
        times = ['<'+str(times[i])+'>' for i in seqs_sort_idx]
        results = results[seqs_sort_idx]
        times[p95] = '≥'+times[p95][1:-1]+'≤'       # 特别标记一下排名第95的时刻
        times = times[p95-20:]
        results = results[p95-20:]
        # 准备画堆叠柱状图
        title = '边缘节点: {}（带宽上限 {}）'.format(eName, bandwidth)
        win = "Node: {}".format(eName)
        viz.bar(
            X=results,
            opts=dict(
                stacked=True, width=620, title=title,
                xlabel='排序后的时刻',
                ylabel='带宽（Band Width）',
                legend=cNames,
                rownames=times,
            ),
            win=win
        )
        # 准备画非堆叠柱状图
        # types=['upper limit', 'occupied']
        # results=np.array([[bandwidth, each] for each in seqs_total]) # 形状为 T*2
        # results = results[p95 - 5:]
        # title = '边缘节点: {} 带宽占用情况'.format(eName)
        # win = eName
        # viz.bar(
        #     X=results,
        #     opts=dict(
        #         stacked=False, width=620, title=title,
        #         xlabel='排序后的时刻',
        #         ylabel='带宽（Band Width）',
        #         legend=types,
        #         rownames=times,
        #     ),
        #     win=win
        # )


viz = visdom.Visdom()

if __name__ == '__main__':
    clientNodes, timeSeqs = getClientNodeAndTimeSeqs()
    T, M = len(timeSeqs), len(clientNodes)
    edgeNodes = getEdgeNodes()
    N = len(edgeNodes)
    plans = readSolutionTxtFile(T, M)  # plans[t][cName]表示在时刻t时，客户节点cName的分配方案
    getEdgeNodeSeqs(plans, edgeNodes, clientNodes, T)
    # 计算成本
    cost, p95 = 0, math.ceil(T * 0.95)-1
    for edgeNode in edgeNodes:
        seqs_total = edgeNode['distributeSeqs_total'].copy()
        seqs_total.sort()
        cost += seqs_total[p95]
    print("成本：{}".format(cost))

    # 画图分析一下
    # analysis(edgeNodes, T)
    # analysis2(edgeNodes, T, sort=False)
    analysis3(edgeNodes, T)

    a = 0
